package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.MarketDeliveryOrderController
import com.zxy.supplier_system.server.controller.MarketDeliveryOrdersItemController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.*
import com.zxy.supplier_system.server.utils.orElse404
import com.zxy.supplier_system.server.utils.toIntIdAndName
import com.zxy.supplier_system.server.utils.toLongIdAndName
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.OffsetDateTime

@Service
class MarketDeliveryOrderService(
    private val marketDeliveryOrderRepository: MarketDeliveryOrderRepository,
    private val employeeService: EmployeeService,
    private val goodsRepository: GoodsRepository,
    private val systemServiceService: SystemServiceService,
    private val marketDeliveryOrderItemRepository: MarketDeliveryOrderItemRepository,
    private val employeeOperateRecordService: EmployeeOperateRecordService,
    private val marketRepository: MarketRepository,
    private val goodsUnitRepository: GoodsUnitRepository,
) {


    @Transactional(readOnly = true)
    fun queryMarketDeliveryOrders(
        request: MarketDeliveryOrderController.QueryMarketDeliveryOrderRequest,
        pageable: Pageable
    ): Page<MarketDeliveryOrderController.QueryMarketDeliveryOrderResponse> {
        val employee = employeeService.getRequiredCurrentEmployee()
        return this.marketDeliveryOrderRepository.findAll(
            (if (employee.authorities.contains(Authority.卖场_订单_查询所有))
                Specification.where(null)
            else
                MarketDeliveryOrderRepository.Specifications.marketSaleManagerEqual(employee.id!!)
                    .or(MarketDeliveryOrderRepository.Specifications.marketShoppingGuidesContains(employee.id!!))).and(
                Specification.allOf(
                    MarketDeliveryOrderRepository.Specifications.createdDateTimeGreaterThanOrEqualTo(request.startCreatedDate),
                    MarketDeliveryOrderRepository.Specifications.createdDateTimeLessThan(request.endCreatedDate?.plusDays(1)),
                    MarketDeliveryOrderRepository.Specifications.validatedDateTimeGreaterThanOrEqualTo(request.validatedStartDate),
                    MarketDeliveryOrderRepository.Specifications.validatedDateTimeLessThanOrEqualTo(request.validatedEndDate),
                    MarketDeliveryOrderRepository.Specifications.marketIn(request.marketIds),
                    MarketDeliveryOrderRepository.Specifications.statusEqual(request.status),
                    MarketDeliveryOrderRepository.Specifications.numberContains(request.number),
                )
            ), pageable
        ).map {
            MarketDeliveryOrderController.QueryMarketDeliveryOrderResponse(
                it.id,
                it.validatedDateTime,
                it.createdDateTime,
                it.market.toIntIdAndName(),
                it.goodsList.size,
                it.status,
                it.number,
            )
        }
    }

    @Transactional(readOnly = true)
    fun getMarketDeliveryOrderDetail(
        id: Int,
        includeItems: Boolean,
        includeComparable: Boolean
    ): MarketDeliveryOrderController.MarketDeliveryOrderDetail {
        val employee = employeeService.getRequiredCurrentEmployee()
        val marketDeliveryOrder = this.marketDeliveryOrderRepository.findByIdOrNull(id).orElse404()
        marketDeliveryOrder.market.checkYouCanSee(employee)
        return MarketDeliveryOrderController.MarketDeliveryOrderDetail(
            marketDeliveryOrder.id,
            marketDeliveryOrder.createdDateTime,
            marketDeliveryOrder.market.toIntIdAndName(),
            if (includeItems)
                marketDeliveryOrder.goodsList.map {
                    MarketDeliveryOrderController.MarketDeliveryOrderItemResponse(
                        it.goods.id,
                        it.goods.name,
                        it.goods.barcode,
                        it.goods.price,
                        it.goods.amount,
                        it.goods.brand?.toIntIdAndName(),
                        it.goods.category?.toIntIdAndName(),
                        it.goods.factory?.toIntIdAndName(),
                        it.amount,
                        it.goods.boxSpecification,
                        it.unit?.toLongIdAndName(),
                        it.goods.units.map { it.toQueryGoodsUnitResponse() },
                        it.goods.registrationCertificateNumber,
                        it.goods.recordFilingNumber,
                        it.goods.expressionPeriod,
                    )
                }
            else
                emptyList(),
            marketDeliveryOrder.validatedDateTime,
            if (includeComparable) {
                if (marketDeliveryOrder.goodsList.size != marketDeliveryOrder.order.goodsList.size) {
                    true
                } else {
                    val marketOrderItemMap = marketDeliveryOrder.order.goodsList.associateBy { it.goods.id }
                    marketDeliveryOrder.goodsList.any {
                        marketOrderItemMap[it.goods.id]?.amount != it.amount
                    }
                }
            } else {
                false
            },
            marketDeliveryOrder.auditedDateTime,
            marketDeliveryOrder.number,
        )
    }

    @Transactional
    fun addItem(
        orderId: Int,
        addMarketDeliveryOrdersItemRequest: MarketDeliveryOrdersItemController.AddMarketDeliveryOrdersItemRequest
    ) {
        val marketDeliveryOrder: MarketDeliveryOrder =
            this.marketDeliveryOrderRepository.findByIdOrNull(orderId).orElse404()
        marketDeliveryOrder.goodsList.add(MarketDeliveryOrderItem().apply {
            this.order = marketDeliveryOrder
            this.goods = goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(
                addMarketDeliveryOrdersItemRequest.goodsId,
                systemServiceService.getCurrentSystemService()
            ).orElse404()
            this.subGoods =
                if (addMarketDeliveryOrdersItemRequest.subGoodsId != null)
                    this.goods.subGoodsList.find { it.id == addMarketDeliveryOrdersItemRequest.subGoodsId }.orElse404()
                else
                    null
            this.amount = addMarketDeliveryOrdersItemRequest.amount
            this.unit = addMarketDeliveryOrdersItemRequest.unitId?.let {
                goodsUnitRepository.findByIdOrNull(it)
            }
            // change in-store
            this.goods.amount -= this.amountWithUnit
            goodsRepository.save(this.goods)
            this.sequence = marketDeliveryOrder.goodsList.nextSequence
        })
        this.marketDeliveryOrderRepository.save(marketDeliveryOrder)
        this.employeeOperateRecordService.addMarketDeliveryOrderItem(marketDeliveryOrder)
    }

    @Transactional
    fun deleteItem(orderId: Int, goodsId: Int) {
        val marketDeliveryOrderItem =
            this.marketDeliveryOrderItemRepository.findByOrder_IdAndGoods_IdAndGoods_SystemService(
                orderId,
                goodsId,
                systemServiceService.getCurrentSystemService()
            ).orElse404()
        marketDeliveryOrderItem.goods.amount += marketDeliveryOrderItem.amount
        this.goodsRepository.save(marketDeliveryOrderItem.goods)
        this.employeeOperateRecordService.deleteMarketDeliveryOrderItem(marketDeliveryOrderItem)
        return this.marketDeliveryOrderItemRepository.delete(marketDeliveryOrderItem)
    }

    @Transactional
    fun updateItemAmount(
        goodsId: Int,
        orderId: Int,
        updateItemAmountRequest: MarketDeliveryOrdersItemController.UpdateItemAmountRequest
    ) {
        val marketDeliveryOrderItem =
            this.marketDeliveryOrderItemRepository.findByOrder_IdAndGoods_IdAndGoods_SystemService(orderId,goodsId,systemServiceService.getCurrentSystemService())
                .orElse404()
        this.goodsRepository.save(marketDeliveryOrderItem.goods)
        val oldAmountWithUnit = marketDeliveryOrderItem.amountWithUnit
        marketDeliveryOrderItem.amount = updateItemAmountRequest.amount
        marketDeliveryOrderItem.unit = updateItemAmountRequest.unitId?.let {
            goodsUnitRepository.findByIdOrNull(it)
        }
        marketDeliveryOrderItem.goods.amount -= marketDeliveryOrderItem.amountWithUnit - oldAmountWithUnit
        this.marketDeliveryOrderItemRepository.save(marketDeliveryOrderItem)
        this.employeeOperateRecordService.updateMarketDeliveryItemAmount(marketDeliveryOrderItem)
    }

    @Transactional
    fun updateItemAmount(
        goodsId: Int,
        orderId: Int,
        subGoodsId: Int,
        updateItemAmountRequest: MarketDeliveryOrdersItemController.UpdateItemAmountRequest
    ) {
        val marketDeliveryOrderItem =
            this.marketDeliveryOrderItemRepository.findByOrder_IdAndGoods_IdAndSubGoods_IdAndGoods_SystemService(orderId,goodsId, subGoodsId,systemServiceService.getCurrentSystemService())
                .orElse404()
        this.goodsRepository.save(marketDeliveryOrderItem.goods)
        val oldAmountWithUnit = marketDeliveryOrderItem.amountWithUnit
        marketDeliveryOrderItem.amount = updateItemAmountRequest.amount
        marketDeliveryOrderItem.unit = updateItemAmountRequest.unitId?.let {
            goodsUnitRepository.findByIdOrNull(it)
        }
        marketDeliveryOrderItem.goods.amount -= marketDeliveryOrderItem.amountWithUnit - oldAmountWithUnit
        this.marketDeliveryOrderItemRepository.save(marketDeliveryOrderItem)
        this.employeeOperateRecordService.updateMarketDeliveryItemAmount(marketDeliveryOrderItem)
    }

    fun queryOrderItemsInOrder(
        orderId: Int,
        pageable: Pageable
    ): Page<MarketDeliveryOrdersItemController.OrderItemInOrderSummary> {
        return this.marketDeliveryOrderItemRepository.findAllByOrder_IdOrderBySequence(orderId, pageable).map {
            val goods = it.goods
            MarketDeliveryOrdersItemController.OrderItemInOrderSummary.fromGoodsAndAmountAndUnit(
                goods,
                it.amount,
                it.unit
            )
        }
    }

    fun exists(orderId: Int, goodsId: Int): Boolean {
        return this.marketDeliveryOrderItemRepository.existsByOrder_IdAndGoods_Id(orderId, goodsId)
    }

    fun exists(orderId: Int, barcode: String): Boolean {
        return this.marketDeliveryOrderItemRepository.existsByOrder_IdAndGoods_Barcode(orderId, barcode)
    }

    @Transactional
    fun addItem(orderId: Int, barcode: String): MarketDeliveryOrdersItemController.OrderItemInOrderSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketDeliveryOrder = this.marketDeliveryOrderRepository.findByIdAndMarket_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByBarcodeAndSystemServiceAndDisableIsFalse(barcode, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(marketDeliveryOrder, goods)
    }

    @Transactional
    fun addItem(orderId: Int, goodsId: Int): MarketDeliveryOrdersItemController.OrderItemInOrderSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketDeliveryOrder = this.marketDeliveryOrderRepository.findByIdAndMarket_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(goodsId, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(marketDeliveryOrder, goods)
    }

    private fun orderItemInOrderSummary(
        marketDeliveryOrder: MarketDeliveryOrder,
        goods: Goods
    ): MarketDeliveryOrdersItemController.OrderItemInOrderSummary {
        val latestItem =
            this.marketDeliveryOrderItemRepository.findFirstByOrder_Market_IdAndGoods_IdOrderByOrder_CreatedDateTimeDesc(
                marketDeliveryOrder.market.id,
                goods.id
            )
        val amount = latestItem?.amount ?: 1
        val goodsUnit = latestItem?.unit ?: goods.basicUnit
        marketDeliveryOrder.goodsList.add(MarketDeliveryOrderItem().apply {
            this.order = marketDeliveryOrder
            this.goods =
                goods
            this.amount = amount
            this.unit = goodsUnit
            this.sequence = marketDeliveryOrder.goodsList.nextSequence
        })
        this.marketDeliveryOrderRepository.save(marketDeliveryOrder)
        this.employeeOperateRecordService.addMarketDeliveryOrderItem(marketDeliveryOrder)
        return MarketDeliveryOrdersItemController.OrderItemInOrderSummary.fromGoodsAndAmountAndUnit(
            goods,
            amount,
            goodsUnit
        )
    }

    @Transactional
    fun validate(id: Int) {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketDeliveryOrder = this.marketDeliveryOrderRepository.findByIdAndMarket_SystemService(
            id,
            currentSystemService
        ).orElse404()
        if (marketDeliveryOrder.validatedDateTime == null) {
            marketDeliveryOrder.validatedDateTime = OffsetDateTime.now()
            this.marketDeliveryOrderRepository.save(marketDeliveryOrder)
            this.employeeOperateRecordService.validateMarketDeliveryOrder(marketDeliveryOrder)
        }
    }

    @Transactional
    fun compareWithOriginalOrder(id: Int): MarketDeliveryOrderController.MarketDeliveryOrderCompareWithOriginalOrderResponse {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketDeliveryOrder =
            this.marketDeliveryOrderRepository.findByIdAndMarket_SystemService(id, currentSystemService).orElse404()
        val goodsList = marketDeliveryOrder.goodsList
        val marketDeliveryOrderItemMap = goodsList.associateBy {
            it.goods.id
        }
        val originalGoodsList = marketDeliveryOrder.order.goodsList
        val marketOrderItemMap = originalGoodsList.associateBy {
            it.goods.id
        }
        val items = originalGoodsList.mapNotNull {
            val goodsId = it.goods.id
            val marketDeliveryOrderItem = marketDeliveryOrderItemMap[goodsId]
            if (marketDeliveryOrderItem == null) {
                MarketDeliveryOrderController.MarketDeliveryOrderCompareWithOriginalOrderItem(
                    goodsId,
                    it.goods.name,
                    it.goods.barcode,
                    it.amount,
                    it.unit?.toQueryGoodsUnitResponse(),
                    null,
                    null
                )
            } else if (marketDeliveryOrderItem.amountWithUnit == it.amountWithUnit) {
                null
            } else {
                MarketDeliveryOrderController.MarketDeliveryOrderCompareWithOriginalOrderItem(
                    goodsId,
                    it.goods.name,
                    it.goods.barcode,
                    it.amount,
                    it.unit?.toQueryGoodsUnitResponse(),
                    marketDeliveryOrderItem.amount,
                    marketDeliveryOrderItem.unit?.toQueryGoodsUnitResponse()
                )
            }
        }.toMutableList()
        items.addAll(goodsList.mapNotNull {
            val goodsId = it.goods.id
            val marketOrderItem = marketOrderItemMap[it.goods.id]
            if (marketOrderItem == null) {
                MarketDeliveryOrderController.MarketDeliveryOrderCompareWithOriginalOrderItem(
                    goodsId,
                    it.goods.name,
                    it.goods.barcode,
                    null,
                    null,
                    it.amount,
                    it.unit?.toQueryGoodsUnitResponse(),
                )
            } else {
                null
            }
        })
        return MarketDeliveryOrderController.MarketDeliveryOrderCompareWithOriginalOrderResponse(
            marketDeliveryOrder.number,
            marketDeliveryOrder.market.id,
            marketDeliveryOrder.market.name,
            items,
        )
    }

    @Transactional
    fun audit(id: Int) {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketDeliveryOrder = this.marketDeliveryOrderRepository.findByIdAndMarket_SystemService(
            id,
            currentSystemService
        ).orElse404()
        if (marketDeliveryOrder.auditedDateTime == null && marketDeliveryOrder.validatedDateTime!=null) {
            marketDeliveryOrder.auditedDateTime = OffsetDateTime.now()
            this.marketDeliveryOrderRepository.save(marketDeliveryOrder)
            this.employeeOperateRecordService.auditMarketDeliveryOrder(marketDeliveryOrder)
            val market = marketDeliveryOrder.market
            val marketGoodsMap = market.goodsList.associateBy { it.goods.id }
            marketDeliveryOrder.goodsList.forEach {
                val marketGoods = marketGoodsMap[it.goods.id]
                if (marketGoods == null) {
                    market.goodsList.add(MarketGoods().apply {
                        this.market = market
                        this.goods = it.goods
                        this.amount = it.amount
                    })
                } else {
                    marketGoods.amount += it.amount
                }
            }
            this.marketRepository.save(market)
        }
    }

    @Transactional
    fun clear(orderId: Int){
        val marketDeliveryOrder = this.marketDeliveryOrderRepository.findByIdAndMarket_SystemService(
            orderId,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        marketDeliveryOrder.goodsList.clear()
        this.marketDeliveryOrderRepository.save(marketDeliveryOrder)
    }
}
